package com.rtsapp.server.logger.spi;

import com.rtsapp.server.logger.Logger;

import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 *  日志工厂, 创建Logger
 *  LoggerManager是线程安全的
 *  createLogger与configure都是线程安全的
 */
public class LoggerManager {

    /**
     * 弱引用形式的, 线程安全的 map
     * 用于存储所有当前创建的Logger
     */
    private final Map<DefaultLogger,DefaultLogger> loggersMap = Collections.synchronizedMap( new WeakHashMap<>() );

    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    private volatile LoggerRepository loggerRepository = null ;

    /**
     * 获得一个名字对应的logger对象
     * @param loggerName
     * @return
     */
    public Logger createLogger( String loggerName ){

        // 加锁的目的主要防止configure正在进行
        try{
            lock.readLock().lock();

            DefaultLogger logger = new DefaultLogger( loggerName );

            // 查找category
            if( loggerRepository == null ) {
                logger.setCategory(null);
            }else{
                LoggerCategory category = loggerRepository.getCategory( loggerName );
                logger.setCategory( category );
            }

            // 加入map
            loggersMap.put( logger, logger );

            return logger;
        }finally {
            lock.readLock().unlock();
        }

    }

    /**
     * 初始化配置
     *
     */
    public  boolean configure( String cfgFile ){

        // 读取配置文件
        LoggerConfig config = new LoggerConfig();
        boolean isOk  = config.parseConfig(cfgFile);
        if( ! isOk ){
            LogLog.error( "日志配置文件:" + cfgFile + "解析不成功" );
            return false;
        }

        LoggerRepository newLoggerRepository = null;
        try {
            newLoggerRepository =  config.createRepository( );
            if( newLoggerRepository ==null ){
                LogLog.error( "日志配置文件:" + cfgFile + "解析不成功" );
                return false;
            }
        } catch (ConfigInvalidException e) {
            LogLog.error( "日志配置文件:" + cfgFile + "解析不成功" , e );
            return false;
        }


        // 变更repository, 阻塞创建新Logger的线程, 但是不阻塞调用日志输出的线程, 但是会丢弃当前要输出的日志(这里有好办法???)
        try{
            // 1. 阻塞创建新Logger的线程
            lock.writeLock().lock();


            // 2. 如果当前LoggerRepository 不为空
            //      1. 对目前所有的Logger解绑Category, 设置为空, 注意( 解绑后到重新绑定前, 日志消息会丢失 )
            //      2. 停止当前的LoggerRepository( 主要是停止线程, 关闭文件输出流 )
            if( loggerRepository != null ){

                for(  DefaultLogger logger :  loggersMap.keySet() ){
                    logger.setCategory( null );
                }

                loggerRepository.shutdown();
            }

            // 3. 创建并设置新的LoggerRepository
            loggerRepository = newLoggerRepository;
            loggerRepository.startup( );

            // 4. 对于目前所有Logger, 重新设置Category
            for( DefaultLogger logger : loggersMap.keySet() ){
                LoggerCategory category = loggerRepository.getCategory( logger.getName( ) );
                logger.setCategory( category );
            }

            return true;

        }catch( Throwable ex ){
            LogLog.error( "重设Logger仓库失败" );
            return false;
        } finally{
            lock.writeLock().unlock();
        }

    }

    /**
     * 设置某一日志的级别
     * @param loggerName
     * @param level
     * @return
     */
    public boolean setLevel( String loggerName, String level ){

        LogLevel logLevel = LogLevel.toLevel( level );
        if( logLevel == null ){
            return false;
        }

        if( loggerRepository == null ){
            return false;
        }

        LoggerCategory category = loggerRepository.getCategory( loggerName );
        if( category != null ){
            category.setLevel( logLevel );
            return true;
        }else{
            return false;
        }
    }


    /**
     * 关闭日志系统
     */
    public void shutdown(){
        if(  loggerRepository != null ){
            loggerRepository.shutdown();
        }
    }

}
